﻿#include "bulletml.h"

BulletMLManager bulletml;

ExpressionDescriptor::ExpressionDescriptor():
type(0), value(0)
{
}

ExpressionDescriptor::~ExpressionDescriptor()
{
}

double ExpressionDescriptor::GetValue()
{
	if (type)
	{
		*rank = global_state.difficulty;
		return exp_parser->Eval();
	}
	else
		return value;
}

void ExpressionDescriptor::Init()
{
	if (type)
	{
		rank = CL_SharedPtr<double>(new double);
		p1 = CL_SharedPtr<double>(new double);
		p2 = CL_SharedPtr<double>(new double);
		p3 = CL_SharedPtr<double>(new double);
		p4 = CL_SharedPtr<double>(new double);
		p5 = CL_SharedPtr<double>(new double);
		p6 = CL_SharedPtr<double>(new double);
		p7 = CL_SharedPtr<double>(new double);
		p8 = CL_SharedPtr<double>(new double);
		p9 = CL_SharedPtr<double>(new double);
		*rank = 0;
		*p1 = 0;
		*p2 = 0;
		*p3 = 0;
		*p4 = 0;
		*p5 = 0;
		*p6 = 0;
		*p7 = 0;
		*p8 = 0;
		*p9 = 0;
		exp_parser->DefineNameChars(_T("$rank0123456789"));
		exp_parser->DefineVar(_T("$rank"), rank.get());
		exp_parser->DefineVar(_T("$1"), p1.get());
		exp_parser->DefineVar(_T("$2"), p2.get());
		exp_parser->DefineVar(_T("$3"), p3.get());
		exp_parser->DefineVar(_T("$4"), p4.get());
		exp_parser->DefineVar(_T("$5"), p5.get());
		exp_parser->DefineVar(_T("$6"), p6.get());
		exp_parser->DefineVar(_T("$7"), p7.get());
		exp_parser->DefineVar(_T("$8"), p8.get());
		exp_parser->DefineVar(_T("$9"), p9.get());
		exp_parser->Eval();
	}
}

BulletDescriptor::BulletDescriptor():
direction_type(0), control_type(0), params_to_pass(0)
{
}

BulletDescriptor::~BulletDescriptor()
{
}

FireDescriptor::FireDescriptor():
direction_type(0), is_bullet_defined(0), params_to_pass(0)
{
}

FireDescriptor::~FireDescriptor()
{
}

BulletMLPattern::BulletMLPattern():
last_bullet_speed(0), last_bullet_direction(0)
{
}

BulletMLPattern::~BulletMLPattern()
{
}

BulletMLManager::BulletMLManager()
{
}

BulletMLManager::~BulletMLManager()
{
}

// Подгружает паттерны из xml файла
void BulletMLManager::LoadPatterns(CL_DomDocument &doc)
{
	cl_log_event("Debug", "Process patterns");
	CL_DomElement root = doc.get_document_element();
	cl_log_event("Debug", "Root element is %1", root.get_node_name());
	if (root.is_null())
		cl_log_event("Error", "No bulletml nodes in document");
	else
	{
		// Обработаем bulletml
		// Может содержать элементы action, fire, bullet
		// Каждый валидный элемент заносится в массив и индексируется.
		//   ---
		// Сначала обрабатываем пули.
		bullets_to_check.clear();
		CL_DomNodeList bulletlist = root.get_elements_by_tag_name("bullet");
		bullet_index = bullets.size();
		for (size_t bi = 0; bi < bulletlist.get_length(); bi++)
		{
			CL_DomNode bullet_node = bulletlist.item(bi);
			CL_DomElement bullet_element = bullet_node.to_element();
			if (bullet_element.is_null())
				continue;
			// В список добавляются только пули, имеющие названия
			if (!bullet_element.has_attribute("label"))
				continue;
			cl_log_event("Debug", "Adding bullet %1", bullet_element.get_attribute("label"));
			BulletDescriptor t_bullet = ProcessBulletElement(bullet_element);
			CL_String b_label(bullet_element.get_attribute("label"));
			bullets.push_back(t_bullet);
			bullet_names[b_label.c_str()] = bullet_index;
			bullet_index++;
		}
		//   ---
		// Теперь выстрелы
		CL_DomNodeList firelist = root.get_elements_by_tag_name("fire");
		fire_index = fires.size();
		for (size_t fi = 0; fi < firelist.get_length(); fi++)
		{
			CL_DomNode fire_node = firelist.item(fi);
			CL_DomElement fire_element = fire_node.to_element();
			if (fire_element.is_null())
				continue;
			// В список добавляются только выстрелы, имеющие названия
			if (!fire_element.has_attribute("label"))
				continue;
			cl_log_event("Debug", "Adding fire %1", fire_element.get_attribute("label"));
			FireDescriptor t_fire = ProcessFireElement(fire_element);
			CL_String f_label(fire_element.get_attribute("label"));
			fires.push_back(t_fire);
			fire_names[f_label.c_str()] = fire_index;
			fire_index++;
		}
		//   ---
		// И последнее, самое главное - действия
		CL_DomNodeList actionlist = root.get_elements_by_tag_name("action");
		pattern_index = patterns.size();
		for (size_t ai = 0; ai < actionlist.get_length(); ai++)
		{
			CL_DomNode action_node = actionlist.item(ai);
			CL_DomElement action_element = action_node.to_element();
			if (action_element.is_null())
				continue;
			// В список добавляются только проименованные действия
			if (!action_element.has_attribute("label"))
				continue;
			cl_log_event("Debug", "Adding action %1", action_element.get_attribute("label"));
			CL_String a_label(action_element.get_attribute("label"));
			CL_SharedPtr<BulletMLPattern> t_action(new BulletMLPattern);
			ProcessActionElement(action_element, t_action.get());
			patterns.push_back(t_action);
			pattern_names[a_label.c_str()] = pattern_index;
			pattern_index++;
		}
	}
	// Теперь проверить зареференные действия, что они существуют
	for (size_t i=0; i< bullets_to_check.size(); i++)
	{
		if (pattern_names[std::string(bullets_to_check[i].second.c_str())] == -1)
		{
			bullets[bullets_to_check[i].first].control_type = 0;
		}
		else
		{
			bullets[bullets_to_check[i].first].action = pattern_names[std::string(bullets_to_check[i].second.c_str())];
		}
	}
	for (size_t i=0; i< actions_to_check.size(); i++)
	{
		if (pattern_names[std::string(actions_to_check[i].second.second.c_str())] == -1)
		{
			patterns[actions_to_check[i].first]->actions[actions_to_check[i].second.first]._param_type2 = 0;
		}
		else
		{
			patterns[actions_to_check[i].first]->actions[actions_to_check[i].second.first].action = pattern_names[std::string(actions_to_check[i].second.second.c_str())];
			patterns[actions_to_check[i].first]->actions[actions_to_check[i].second.first].param_location += patterns[patterns[actions_to_check[i].first]->actions[actions_to_check[i].second.first].action]->params_needed;
		}
	}
	// Подсчитать количество параметров, требуемое для каждого действа
	for (size_t i=0; i< patterns.size(); i++)
	{
		int bpars = 0;
		int tpars = 0;
		for (size_t j=0; j< patterns[i]->actions.size(); j++)
		{
			tpars = patterns[i]->actions[j].param_location;
			patterns[i]->actions[j].param_location = bpars;
			bpars += tpars;
		}
		patterns[i]->params_needed = bpars;
	}
}

// Обрабатывает элемент описания пули
// Может содержать элементы speed, direction, action, actionRef
BulletDescriptor BulletMLManager::ProcessBulletElement(CL_DomElement &el)
{
	BulletDescriptor bul;
	// Скорость
	CL_DomNodeList blist = el.get_elements_by_tag_name("speed");
	if (blist.get_length() > 0)
	{
		CL_DomElement speed_element = blist.item(0).to_element();
		CL_String speed_number(speed_element.get_text());
		cl_log_event("Debug", "Speed parameter %1", speed_number);
		bul.speed = GetParsedExpression(speed_number);
		cl_log_event("Debug", "Parsed speed %1", bul.speed.GetValue());
	}
	// Направление
	blist = el.get_elements_by_tag_name("direction");
	if (blist.get_length() > 0)
	{
		CL_DomElement dir_element = blist.item(0).to_element();
		CL_String dir_number(dir_element.get_text());
		CL_String dir_type(dir_element.get_attribute("type"));
		cl_log_event("Debug", "Direction parameter %1, type %2", dir_number, dir_type);
		bul.direction = GetParsedExpression(dir_number);
		cl_log_event("Debug", "Parsed direction %1", bul.direction.GetValue());
		if (dir_type == "aim")
			bul.direction_type = 0;
		if (dir_type == "absolute")
			bul.direction_type = 1;
		if (dir_type == "relative")
			bul.direction_type = 2;
		if (dir_type == "sequence")
			bul.direction_type = 3;
	}
	// Действие
	// ================= TO DO ===============
	CL_DomElement action_element;
	blist = el.get_elements_by_tag_name("actionRef");
	if (blist.get_length() > 0)
	{
		// Добавим действие, которое будет определено потом
		// И добавим дальнейшую проверку этого действия
		CL_DomElement ar_element = blist.item(0).to_element();
		if (!ar_element.is_null())
		{
			// Рефериться могут только действия, имеющие названия
			if (ar_element.has_attribute("label"))
			{
				CL_String actionref_label(ar_element.get_attribute("label"));
				cl_log_event("Debug", "Adding actionRef %1 to bullet element", actionref_label);
				pattern_names[std::string(actionref_label.c_str())] = -1;
				// Поставить пулю на вид
				bullets_to_check.push_back(std::pair<int, CL_String>(bullet_index, actionref_label)); // Чтобы потом проверить соответствие
				CL_DomNodeList param_list = ar_element.get_elements_by_tag_name("param");
				if (param_list.get_length() > 0)
				{
					// Список параметров, передающихся в действие
					int par = 0;
					while (par<param_list.get_length() && par<10)
					{
						bul.params[par] = GetParsedExpression(param_list.item(par).to_element().get_text());
						par++;
					}
					bul.params_to_pass = par;
				}
				bul.control_type = 1;
				bul.action = -1;
			}
		}
	}
	else
	{
		blist = el.get_elements_by_tag_name("action");
		if (blist.get_length() > 0)
		{
			// Эксклюзивное действие только для этой пули
			action_element = blist.item(0).to_element();
			if (!action_element.is_null())
			{
				cl_log_event("Debug", "Adding action to bullet element");
				CL_SharedPtr<BulletMLPattern> t_action(new BulletMLPattern);
				ProcessActionElement(action_element, t_action.get());
				patterns.push_back(t_action);
				bul.control_type = 1;
				bul.action = pattern_index;
				pattern_index++;
			}
		}
	}
	// Графика?
	// ================= TO DO ===============
	return bul;
}

// Обрабатывает элемент описания выстрела
// Может содержать элементы speed, direction, bullet, bulletRef
FireDescriptor BulletMLManager::ProcessFireElement(CL_DomElement &el)
{
	FireDescriptor fire;
	// Скорость
	CL_DomNodeList flist = el.get_elements_by_tag_name("speed");
	if (flist.get_length() > 0)
	{
		CL_DomElement speed_element = flist.item(0).to_element();
		CL_String speed_number(speed_element.get_text());
		cl_log_event("Debug", "Speed parameter %1", speed_number);
		fire.speed = GetParsedExpression(speed_number);
		cl_log_event("Debug", "Parsed speed %1", fire.speed.GetValue());
	}
	// Направление
	flist = el.get_elements_by_tag_name("direction");
	if (flist.get_length() > 0)
	{
		CL_DomElement dir_element = flist.item(0).to_element();
		CL_String dir_number(dir_element.get_text());
		CL_String dir_type(dir_element.get_attribute("type"));
		cl_log_event("Debug", "Direction parameter %1, type %2", dir_number, dir_type);
		fire.direction = GetParsedExpression(dir_number);
		cl_log_event("Debug", "Parsed direction %1", fire.direction.GetValue());
		if (dir_type == "aim")
			fire.direction_type = 0;
		if (dir_type == "absolute")
			fire.direction_type = 1;
		if (dir_type == "relative")
			fire.direction_type = 2;
		if (dir_type == "sequence")
			fire.direction_type = 3;
	}
	// Пуля
	int bullet_found = -1;
	flist = el.get_elements_by_tag_name("bulletRef");
	if (flist.get_length() > 0)
	{
		for (size_t i = 0; i < flist.get_length(); i++)
		{
			CL_DomNode br_node = flist.item(i);
			CL_DomElement br_element = br_node.to_element();
			if (br_element.is_null())
				continue;
			// Рефериться могут только пули, имеющие названия
			if (!br_element.has_attribute("label"))
				continue;
			CL_String bulletref_label(br_element.get_attribute("label"));
			if (bullet_names.find(std::string(bulletref_label.c_str())) != bullet_names.end())
			{
				cl_log_event("Debug", "Adding bulletRef %1 to fire element", bulletref_label);
				bullet_found = bullet_names[std::string(bulletref_label.c_str())];
				CL_DomNodeList param_list = br_element.get_elements_by_tag_name("param");
				if (param_list.get_length() > 0)
				{
					// Список параметров, передающихся в пулю
					int par = 0;
					while (par<param_list.get_length() && par<10)
					{
						fire.params[par] = GetParsedExpression(param_list.item(par).to_element().get_text());
						par++;
					}
					fire.params_to_pass = par;
				}
				break;
			}
		}
	}
	if (bullet_found < 0)
	{
		flist = el.get_elements_by_tag_name("bullet");
		if (flist.get_length() > 0)
		{
			CL_DomElement bullet_element = flist.item(0).to_element();
			if (!bullet_element.is_null())
			{
				cl_log_event("Debug", "Adding bullet to fire element");
				BulletDescriptor t_bullet = ProcessBulletElement(bullet_element);
				bullet_found = bullets.size();
				bullets.push_back(t_bullet);
				bullet_index++;
			}
		}
	}
	if (bullet_found >= 0)
	{
		fire.is_bullet_defined = 1;
		fire.bullet = bullet_found;
	}
	return fire;
}

// Обрабатывает элемент описания действия
// Может содержать элементы repeat, fire, fireRef, changeSpeed, changeDirection, accel, wait, kill, action, actionRef, move, effect
void BulletMLManager::ProcessActionElement(CL_DomElement &el, BulletMLPattern *action)
{
	action->params_needed = 0;
	action->actions.clear();
	std::vector<std::pair<int,CL_String>> local_actions_to_check;
	CL_DomNodeList n_list = el.get_child_nodes();
	for (size_t i=0; i< n_list.get_length(); i++)
	{
		CL_DomElement a = n_list.item(i).to_element();
		if (a.get_node_name() == "kill")
		{
			// Действие - уничтожить пулю
			cl_log_event("Debug", "Adding action kill");
			ActionDescriptor t_action;
			t_action.type = 0;
			t_action.param_location =0;
			action->actions.push_back(t_action);
		}
		if (a.get_node_name() == "wait")
		{
			// Действие - ждать n фреймов
			cl_log_event("Debug", "Adding action wait");
			ActionDescriptor t_action;
			t_action.type = 1;
			t_action._param = GetParsedExpression(a.get_text());
			t_action.param_location = 2;
			action->actions.push_back(t_action);
		}
		if (a.get_node_name() == "changeSpeed")
		{
			// Действие - менять скорость на протяжении n фреймов
			// Может содержать значения speed, duration
			if (a.get_elements_by_tag_name("speed").get_length() > 0)
			{
				cl_log_event("Debug", "Adding action changeSpeed");
				ActionDescriptor t_action;
				t_action.type = 2;
				CL_DomElement speed_element = a.get_elements_by_tag_name("speed").item(0).to_element();
				t_action._param = GetParsedExpression(speed_element.get_text());
				t_action._param_type = 0;
				if (speed_element.has_attribute("type"))
				{
					CL_String speed_type(speed_element.get_attribute("type"));
					if (speed_type == "absolute")
						t_action._param_type = 0;
					if (speed_type == "relative")
						t_action._param_type = 1;
					if (speed_type == "sequence")
						t_action._param_type = 2;
				}
				t_action._param_type2 = 0;
				if (a.get_elements_by_tag_name("duration").get_length() > 0)
				{
					CL_DomElement d_element = a.get_elements_by_tag_name("duration").item(0).to_element();
					t_action._param2 = GetParsedExpression(d_element.get_text());
					t_action._param_type2 = 1;
				}
				t_action.param_location = 4;
				action->actions.push_back(t_action);
			}
		}
		if (a.get_node_name() == "changeDirection")
		{
			// Действие - менять направление на протяжении n фреймов
			// Может содержать значения direction, duration
			if (a.get_elements_by_tag_name("direction").get_length() > 0)
			{
				cl_log_event("Debug", "Adding action changeDirection");
				ActionDescriptor t_action;
				t_action.type = 3;
				CL_DomElement dir_element = a.get_elements_by_tag_name("direction").item(0).to_element();
				t_action._param = GetParsedExpression(dir_element.get_text());
				t_action._param_type = 0;
				if (dir_element.has_attribute("type"))
				{
					CL_String speed_type(dir_element.get_attribute("type"));
					if (speed_type == "aim")
						t_action._param_type = 0;
					if (speed_type == "absolute")
						t_action._param_type = 1;
					if (speed_type == "relative")
						t_action._param_type = 2;
					if (speed_type == "sequence")
						t_action._param_type = 3;
				}
				t_action._param_type2 = 0;
				if (a.get_elements_by_tag_name("duration").get_length() > 0)
				{
					CL_DomElement d_element = a.get_elements_by_tag_name("duration").item(0).to_element();
					t_action._param2 = GetParsedExpression(d_element.get_text());
					t_action._param_type2 = 1;
				}
				t_action.param_location = 2;
				action->actions.push_back(t_action);
			}
		}
		if (a.get_node_name() == "repeat")
		{
			// Действие - повторять действие n раз
			// Может содержать значения times, action|actionRef
			cl_log_event("Debug", "Adding action repeat");
			ActionDescriptor t_action;
			t_action.type = 4;
			t_action._param_type = 0;
			if (a.get_elements_by_tag_name("times").get_length() > 0)
			{
				CL_DomElement times_element = a.get_elements_by_tag_name("times").item(0).to_element();
				t_action._param = GetParsedExpression(times_element.get_text());
				t_action._param_type = 1;
			}
			int additional_params_needed = 0;
			t_action._param_type2 = 0;
			if (a.get_elements_by_tag_name("action").get_length() > 0)
			{
				CL_DomElement a_element = a.get_elements_by_tag_name("action").item(0).to_element();
				if (!a_element.is_null())
				{
					cl_log_event("Debug", "Adding action to repeat");
					CL_SharedPtr<BulletMLPattern> t_a(new BulletMLPattern);
					ProcessActionElement(a_element, t_a.get());
					t_action._param_type2 = 1;
					t_action.action = patterns.size();
					patterns.push_back(t_a);
					pattern_index++;
					additional_params_needed = t_a->params_needed;
				}
			}
			else if (a.get_elements_by_tag_name("actionRef").get_length() > 0)
			{
				CL_DomElement ar_element = a.get_elements_by_tag_name("actionRef").item(0).to_element();
				if (!ar_element.is_null())
				{
					// Рефериться могут только действия, имеющие названия
					if (ar_element.has_attribute("label"))
					{
						CL_String actionref_label(ar_element.get_attribute("label"));
						cl_log_event("Debug", "Adding actionRef %1 to repeat element", actionref_label);
						t_action._param_type2 = 2;
						if (pattern_names.find(std::string(actionref_label.c_str())) != pattern_names.end())
						{
							// Действие уже есть, добавляем
							if (pattern_names[std::string(actionref_label.c_str())] == -1)
							{
								// Действие добавлено другим рефералом, нужно будет проверить потом
								t_action.action = -1;
								local_actions_to_check.push_back(std::pair<int, CL_String>(action->actions.size(), actionref_label)); // Чтобы потом проверить соответствие
							}
							else
							{
								// Действие существует
								t_action.action = pattern_names[std::string(actionref_label.c_str())];
								additional_params_needed = patterns[t_action.action]->params_needed;
							}
						}
						else
						{
							// Поставить действие на вид
							pattern_names[std::string(actionref_label.c_str())] = -1;
							local_actions_to_check.push_back(std::pair<int, CL_String>(action->actions.size(), actionref_label)); // Чтобы потом проверить соответствие
							t_action.action = -1;
						}
						CL_DomNodeList param_list = ar_element.get_elements_by_tag_name("param");
						if (param_list.get_length() > 0)
						{
							// Список параметров, передающихся в действие
							int par = 0;
							while (par<param_list.get_length() && par<10)
							{
								t_action.params[par] = GetParsedExpression(param_list.item(par).to_element().get_text());
								par++;
							}
							t_action.params_to_pass = par;
						}
					}
				}
			}
			t_action.param_location = 1 + additional_params_needed;
			action->actions.push_back(t_action);
		}
		if (a.get_node_name() == "fire")
		{
			// Действие - запустить выстрел
			cl_log_event("Debug", "Adding action fire");
			ActionDescriptor t_action;
			t_action.type = 5;
			FireDescriptor t_fire = ProcessFireElement(a);
			fires.push_back(t_fire);
			t_action.action = fire_index;
			fire_index++;
			t_action.param_location = 0;
			action->actions.push_back(t_action);
		}
		if (a.get_node_name() == "fireRef")
		{
			// Действие - запустить выстрел
			if (a.has_attribute("label"))
			{
				CL_String f_label(a.get_attribute("label"));
				if (fire_names.find(f_label.c_str()) != fire_names.end())
				{
					ActionDescriptor t_action;
					t_action.type = 6;
					cl_log_event("Debug", "Adding action fireRef");
					t_action.action = fire_names[f_label.c_str()];
					CL_DomNodeList param_list = a.get_elements_by_tag_name("param");
					if (param_list.get_length() > 0)
					{
						// Список параметров, передающихся в выстрел
						int par = 0;
						while (par<param_list.get_length() && par<10)
						{
							t_action.params[par] = GetParsedExpression(param_list.item(par).to_element().get_text());
							par++;
						}
						t_action.params_to_pass = par;
					}
					t_action.param_location = 0;
					action->actions.push_back(t_action);
				}
			}
		}
		if (a.get_node_name() == "action")
		{
			// Действие - запустить другое действие
			cl_log_event("Debug", "Adding action action");
			ActionDescriptor t_action;
			t_action.type = 7;
			CL_SharedPtr<BulletMLPattern> t_a(new BulletMLPattern);
			ProcessActionElement(a, t_a.get());
			t_action._param_type2 = 1;
			t_action.action = patterns.size();
			patterns.push_back(t_a);
			pattern_index++;
			t_action.param_location = t_a->params_needed;
			action->actions.push_back(t_action);
		}
		if (a.get_node_name() == "actionRef")
		{
			// Действие - запустить имеющееся действие
			if (a.has_attribute("label"))
			{
				ActionDescriptor t_action;
				cl_log_event("Debug", "Adding action actionRef");
				int additional_params_needed = 0;
				t_action.type = 8;
				CL_String actionref_label(a.get_attribute("label"));
				t_action._param_type2 = 2;
				if (pattern_names.find(std::string(actionref_label.c_str())) != pattern_names.end())
				{
					// Действие уже есть, добавляем
					if (pattern_names[std::string(actionref_label.c_str())] == -1)
					{
						// Действие добавлено другим рефералом, нужно будет проверить потом
						t_action.action = -1;
						local_actions_to_check.push_back(std::pair<int, CL_String>(action->actions.size(), actionref_label)); // Чтобы потом проверить соответствие
					}
					else
					{
						// Действие существует
						t_action.action = pattern_names[std::string(actionref_label.c_str())];
						additional_params_needed = patterns[t_action.action]->params_needed;
					}
				}
				else
				{
					// Поставить действие на вид
					pattern_names[std::string(actionref_label.c_str())] = -1;
					local_actions_to_check.push_back(std::pair<int, CL_String>(action->actions.size(), actionref_label)); // Чтобы потом проверить соответствие
					t_action.action = -1;
				}
				CL_DomNodeList param_list = a.get_elements_by_tag_name("param");
				if (param_list.get_length() > 0)
				{
					// Список параметров, передающихся в действие
					int par = 0;
					while (par<param_list.get_length() && par<10)
					{
						t_action.params[par] = GetParsedExpression(param_list.item(par).to_element().get_text());
						par++;
					}
					t_action.params_to_pass = par;
				}
				t_action.param_location = additional_params_needed;
				action->actions.push_back(t_action);
			}
		}
		if (a.get_node_name() == "accel")
		{
			// Действие - ускорение на протяжении n фреймов
			// Содержит значения direction, duration, value
			cl_log_event("Debug", "Adding action accel");
			ActionDescriptor t_action;
			t_action.type = 9;
			t_action.action = 0;
			if (a.has_attribute("type"))
			{
				CL_String a_type(a.get_attribute("type"));
				if (a_type == "absolute")
					t_action.action = 0;
				if (a_type == "relative")
					t_action.action = 1;
				if (a_type == "sequence")
					t_action.action = 2;
			}
			t_action._param_type = 0;
			if (a.get_elements_by_tag_name("value").get_length() > 0)
			{
				CL_DomElement d_element = a.get_elements_by_tag_name("value").item(0).to_element();
				t_action._param = GetParsedExpression(d_element.get_text());
				t_action._param_type = 1;
			}
			t_action._param_type2 = 0;
			if (a.get_elements_by_tag_name("duration").get_length() > 0)
			{
				CL_DomElement d_element = a.get_elements_by_tag_name("duration").item(0).to_element();
				t_action._param2 = GetParsedExpression(d_element.get_text());
				t_action._param_type2 = 1;
			}
			t_action._param_type3 = 0;
			if (a.get_elements_by_tag_name("direction").get_length() > 0)
			{
				CL_DomElement d_element = a.get_elements_by_tag_name("direction").item(0).to_element();
				t_action._param3 = GetParsedExpression(d_element.get_text());
				t_action._param_type3 = 3;
				if (d_element.has_attribute("type"))
				{
					CL_String dd_type(d_element.get_attribute("type"));
					if (dd_type == "aim")
						t_action._param_type3 = 1;
					if (dd_type == "absolute")
						t_action._param_type3 = 2;
					if (dd_type == "relative")
						t_action._param_type3 = 3;
					if (dd_type == "sequence")
						t_action._param_type3 = 4;
				}
				t_action._param_type3 = 1;
			}
			t_action.param_location = 1;
			action->actions.push_back(t_action);
		}
		if (a.get_node_name() == "effect")
		{
			// Запустить эффект
		}
		if (a.get_node_name() == "move")
		{
			// Действие - двигаться в указанную точку за n фреймов
		}
	}
	for (size_t i=0; i< local_actions_to_check.size(); i++)
	{
		actions_to_check.push_back(std::pair<int, std::pair<int, CL_String>>(patterns.size(), local_actions_to_check[i]));
	}
}

// Парсит выражение из строки
ExpressionDescriptor BulletMLManager::GetParsedExpression(CL_String &expr)
{
	ExpressionDescriptor res;
	CL_String16 expr16(expr.c_str());
	if ((expr.find("$rank") != -1) || (expr.find("$1") != -1) || (expr.find("$2") != -1) || (expr.find("$3") != -1) || (expr.find("$4") != -1) || (expr.find("$5") != -1) || (expr.find("$6") != -1) || (expr.find("$7") != -1) || (expr.find("$8") != -1) || (expr.find("$9") != -1))
	{
		// Expression
		res.type = 1;
		CL_SharedPtr<Parser> new_parser(new Parser);
		res.exp_parser = new_parser;
		res.exp_parser->SetExpr(expr16);
		res.Init();
	}
	else
	{
		// Number
		parser.SetExpr(expr16);
		res.value = parser.Eval();
	}
	return res;
}



BulletMLPattern* BulletMLManager::GetPatternByID(int p_id)
{
	if (p_id < patterns.size())
		return patterns[p_id].get();
	else
		return 0;
}

BulletMLPattern* BulletMLManager::GetPatternByName(CL_String name)
{
	std::string nn(name.c_str());
	if (pattern_names.find(nn) != pattern_names.end())
		return patterns[pattern_names[nn]].get();
	else
		return 0;
}

// Инициализация параметров
void BulletMLPattern::InitGameObject(GameObject *target)
{
	if (params_needed > 0)
	{
		target->pattern_params = new double[params_needed];
		for(int i=0; i < params_needed; i++)
			target->pattern_params[i] = 0;
	}
}

// Главная функция управления пулями
void BulletMLPattern::Update(Bullet *b)
{
	int c_a = b->control_cycle; // current_action
	int offset = b->params_passed;
	ActionDescriptor* a;
	bool wait_order = true; // Есть невыполненные действия
	while ((c_a < actions.size()) && wait_order)
	{
		a = &actions[c_a];
		if (a->type == 0)
		{
			// kill; уничтожить пулю
			b->use_pattern = 0; // Выключить паттерн
			b->active = 0;
		}
		else if (a->type == 1)
		{
			// wait; ожидать n фреймов
			// param 0 - количество прошедших фреймов
			// param 1 - количество фреймов, которое нужно ожидать
			if (b->pattern_params[offset + a->param_location] == 0)
			{
				// инициализация цикла ожидания
				if (a->_param.type)
					FillExpressionParameters(a->_param, b);
				b->pattern_params[offset + a->param_location + 1] = a->_param.GetValue();
			}
			b->pattern_params[a->param_location] += 1; // прибавляем один фрейм
			// и проверямем, истекло ли время ожидания
			if (b->pattern_params[offset + a->param_location] > b->pattern_params[offset + a->param_location + 1])
			{
				// Ваше время истекло, следующее действие
				c_a++;
			}
			else
			{
				// Действия на этот фрейм закончились, ожидаем
				wait_order = false;
			}
		}
		else if (a->type == 2)
		{
			// changeSpeed; изменить скорость за N фреймов
			// param 0 - количество прошедших фреймов
			// param 1 - количество фреймов, за которые производится изменение
			// param 2 - финальный результат
			// param 3 - дельта изменения на 1 фрейм
			if (b->pattern_params[offset + a->param_location] == 0)
			{
				// Инициализация
				if (a->_param_type2)
				{
					// параметр времени
					FillExpressionParameters(a->_param2, b);
					b->pattern_params[offset + a->param_location + 1] = a->_param2.GetValue();
				}
				// параметр скорости
				FillExpressionParameters(a->_param, b);
				b->pattern_params[offset + a->param_location + 2] = a->_param.GetValue();
				if (a->_param_type == 1)
				{
					// Изменение скорости относительное
					b->pattern_params[offset + a->param_location + 2] += b->speed;
				}
				else if (a->_param_type == 2)
				{
					// Изменение в последовательности
					b->pattern_params[offset + a->param_location + 2] += last_bullet_speed;
				}
				if (b->pattern_params[offset + a->param_location + 1] > 0)
				{
					// Меняем скорость за положительное число фреймов
					b->pattern_params[offset + a->param_location + 3] = (b->pattern_params[offset + a->param_location + 2] - b->speed) / b->pattern_params[offset + a->param_location + 1];
				}
			}
			b->pattern_params[offset + a->param_location] += 1;
			if (b->pattern_params[offset + a->param_location] > b->pattern_params[offset + a->param_location + 1])
			{
				// Закончили обработку действия, переходим к следующему
				b->speed = b->pattern_params[offset + a->param_location + 2];
				c_a++;
			}
			else
			{
				// Изменяем скорость на этот фрейм
				b->speed += b->pattern_params[offset + a->param_location + 3];
				// Действия на этот фрейм закончились
				wait_order = false;
			}
		}
		else if (a->type == 3)
		{
			// changeDirection; изменить направление за N фреймов
			// param 0 - количество прошедших фреймов
			// param 1 - количество фреймов, за которые производится изменение
			// param 2 - финальный результат
			// param 3 - дельта изменения на 1 фрейм
		}
		else if (a->type == 4)
		{
			// repeat; повторить действие N раз
		}
		else if (a->type == 5)
		{
			// fire; произвести выстрел новой пули с заданными параметрами
		}
		else if (a->type == 6)
		{
			// fireRef; запустить обработчик огня и передать ему параметры
		}
		else if (a->type == 7)
		{
			// action; запустить новое действие
		}
		else if (a->type == 8)
		{
			// actionRef; запустить новое действие и передать ему параметры
		}
		else if (a->type == 9)
		{
			// accel; приложить к пуле заданное ускорение на N фреймов
		}
		// ========== TO DO ==========
		// Добавить запоминание скорости и направления обработанной пули
	}
	// Все ли действия выполнены
	if (c_a < actions.size())
	{
		// ещё не все, продолжаем разговор
		b->control_cycle = c_a;
	}
	else
	{
		// пуля становится просто пулей
		b->use_pattern = 0;
	}
}

// Заполнить параметры выражения из стартовых параметров
// Стартовые параметры идут первыми в pattern_params, до параметров действий
void BulletMLPattern::FillExpressionParameters(ExpressionDescriptor &ex, GameObject *b)
{
	if (b->params_passed > 0)
		*(ex.p1) = b->pattern_params[0];
	else
		*(ex.p1) = 0;
	if (b->params_passed > 1)
		*(ex.p2) = b->pattern_params[1];
	else
		*(ex.p2) = 0;
	if (b->params_passed > 2)
		*(ex.p3) = b->pattern_params[2];
	else
		*(ex.p3) = 0;
	if (b->params_passed > 3)
		*(ex.p4) = b->pattern_params[3];
	else
		*(ex.p4) = 0;
	if (b->params_passed > 4)
		*(ex.p5) = b->pattern_params[4];
	else
		*(ex.p5) = 0;
	if (b->params_passed > 5)
		*(ex.p6) = b->pattern_params[5];
	else
		*(ex.p6) = 0;
	if (b->params_passed > 6)
		*(ex.p7) = b->pattern_params[6];
	else
		*(ex.p7) = 0;
	if (b->params_passed > 7)
		*(ex.p8) = b->pattern_params[7];
	else
		*(ex.p8) = 0;
	if (b->params_passed > 8)
		*(ex.p9) = b->pattern_params[8];
	else
		*(ex.p9) = 0;
}
